﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.Parser.Tests.Peg
{
    [TestFixture]
    public class DynamicChoiceProductionSpecification {
        [Test]
        public void ShouldParseChoicesInOrder () {
            var context = new ParseContext ();
            var dynamicChoice = new DynamicChoiceProduction ();

            var choiceLog = new List<string> ();

            var choice1 = new FakeLoggingProduction (choiceLog, "first", null);
            var choice2 = new FakeLoggingProduction (choiceLog, "second", null);
            var choice3 = new FakeLoggingProduction (choiceLog, "third", null);
            context.DynamicGrammar.AddDynamicChoice (dynamicChoice, new[] {choice1, choice2, choice3});

            Assert.IsNull(dynamicChoice.Parse ("abc", 0, context));

            Assert.AreEqual (new [] {"first", "second", "third"}, choiceLog.ToArray ());
        }

        [Test]
        public void ReturnFirstSuccessfulParseResult () {
            var context = new ParseContext();
            var dynamicChoice = new DynamicChoiceProduction ();

            var choiceLog = new List<string> ();

            var choice1 = new FakeLoggingProduction (choiceLog, "first", null);
            var result = new ParseResult (2, null, null);
            var choice2 = new FakeLoggingProduction (choiceLog, "second", result);
            var choice3 = new FakeLoggingProduction (choiceLog, "third", null);
            context.DynamicGrammar.AddDynamicChoice (dynamicChoice, new[] {choice1, choice2, choice3});

            Assert.AreEqual(result, dynamicChoice.Parse ("abc", 0, context));

            Assert.AreEqual (new [] {"first", "second"}, choiceLog.ToArray ());
        }

        [Test]
        public void ShouldThrowExceptionIfProductionNotFound () {
            var context = new ParseContext();
            context.DynamicGrammar.AddDynamicChoice ("this won't be found", new IProduction [0]);

            var production = new DynamicChoiceProduction();
            Assert.Throws<KeyNotFoundException> (() => production.Parse ("abc", 0, context));
        }

        class FakeLoggingProduction : IProduction {
            private readonly List<string> Log;
            public string Name { get; set; }
            private readonly ParseResult Result;

            public FakeLoggingProduction (List<string> log, string name, ParseResult result) {
                Log = log;
                Name = name;
                Result = result;
            }

            public IErrorInformation ErrorInformation { get; set; }
            public Dictionary<int, ParseResult> MemoTable { get; private set; }

            public bool IsIncludedInErrors { get; set; }

            public bool IsInfix {
                get {
                    return false;
                }
            }

            public bool IsLeftRecursive {
                get {
                    return false;
                }
            }

            public IInfixInformation InfixInformation {
                get { return new NotInfixInformation (); }
            }

            public Dictionary<int, ParseResult> GetMemoTable (char [] source) {
                return null;
            }

            public ParseResult Parse (char[] source, int index, ParseContext context, string sourceString, ParseEnvironment parseEnvironment) {
                Log.Add (Name);
                return Result;
            }

            public Yield Parse(char[] source, int index, ParseContext context, string sourceString, ParseEnvironment parseEnvironment, Func<ParseResult, Yield> continuation)
            {
                return () => continuation (Parse(source, index, context, sourceString, parseEnvironment));
            }
        }
    }
}
